
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       soft_sigreader_int.c
  * @author     baiyang
  * @date       2022-6-22
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "soft_sigreader_int.h"

#include <board.h>
#include <rthw.h>

#if defined(HAL_USE_EICU)
/*-----------------------------------macro------------------------------------*/
#define EXAMPLE_CAPT_IRQN               (INT002_IRQn)
#define EXAMPLE_CAPT_INT_SRC            (INT_SRC_TMR6_8_GCMP_B)

#define TMR6_8_PWMB_PORT                (GPIO_PORT_C)
#define TMR6_8_PWMB_PIN                 (GPIO_PIN_08)

/* Specifies clock divider that you need. @ref TMRA_Clock_Divider */
#define TMR6_8_CLK_DIV                  (TMR6_CLK_DIV128)
/*----------------------------------typedef-----------------------------------*/
typedef struct soft_sigreader_int* soft_sigreader_int_t;

#pragma pack(1)
typedef struct {
    uint16_t w0;
    uint16_t w1;
} ssi_pulse_t;
#pragma pack()

struct soft_sigreader_int {
    uint16_t head, tail;
    ssi_pulse_t sigbuf_pool[SOFTSIG_MAX_SIGNAL_TRANSITIONS];

    uint16_t last_value;
    float pulse_scale;     // tick -> us
    uint16_t pulse_2ms;    // 1ms -> tick
};

/*---------------------------------prototype----------------------------------*/
static void Tmr6_8_CapInputCallBack(void);
static void ConfigCapture(CM_TMR6_TypeDef *TMR6x);
static float get_tmr6_input_capture_time_scale(CM_TMR6_TypeDef *TMR6x);
/*----------------------------------variable----------------------------------*/
static uint32_t u32CaptureA;
static uint32_t u32CaptureB;
static int32_t pin_state;
static ssi_pulse_t capinput_pulse;

static struct soft_sigreader_int ssi;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
static inline void sigbuf_init()
{
    ssi.head = ssi.tail = 0;
}

static inline void sigbuf_put(ssi_pulse_t *pulse)
{
    uint16_t next = (ssi.head + 1) % SOFTSIG_MAX_SIGNAL_TRANSITIONS;

    if (next != ssi.tail) {
        ssi.sigbuf_pool[ssi.head] = (*pulse);
        ssi.head = next;
    }
}

static inline void sigbuf_get(ssi_pulse_t *pulse)
{
    if (ssi.tail != ssi.head) {
        *pulse = ssi.sigbuf_pool[ssi.tail];
        ssi.tail = (ssi.tail + 1) % SOFTSIG_MAX_SIGNAL_TRANSITIONS;
    }
}

static inline uint16_t sigbuf_data_len()
{
    uint16_t head = ssi.head;

    if (head == ssi.tail) {
        return 0;
    } else if (head > ssi.tail) {
        return head - ssi.tail;
    } else {
        return SOFTSIG_MAX_SIGNAL_TRANSITIONS - (ssi.tail - head);
    }
}

void ssi_init()
{
    sigbuf_init();

    FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR6_8, ENABLE);

    /* Timer6 PWM output pin configuration */
    GPIO_SetFunc(TMR6_8_PWMB_PORT, TMR6_8_PWMB_PIN, GPIO_FUNC_3);

    /* Config CM_TMR6_2 capture in */
    ConfigCapture(CM_TMR6_8);

    /* Start timer6 */
    //TMR6_Start(CM_TMR6_8);
}

bool ssi_read(uint32_t *widths0, uint32_t *widths1)
{
    if (sigbuf_data_len() >= 2) {
        ssi_pulse_t pulse;
        sigbuf_get(&pulse);

        *widths0 = (uint16_t)(pulse.w0 * ssi.pulse_scale);
        *widths1 = (uint16_t)((pulse.w1 - pulse.w0) * ssi.pulse_scale);
        return true;
    }

    return false;
}

/**
 * @brief  TIMER6 capture input interrupt handler callback.
 * @param  None
 * @retval None
 */
static void Tmr6_8_CapInputCallBack(void)
{
    rt_base_t level = rt_hw_interrupt_disable();

    u32CaptureA = CM_TMR6_8->GCMAR;
    u32CaptureB = CM_TMR6_8->GCMBR;

    rt_hw_interrupt_enable(level);

    pin_state = GPIO_ReadInputPins(TMR6_8_PWMB_PORT, TMR6_8_PWMB_PIN);

    capinput_pulse.w0 = u32CaptureA;
    capinput_pulse.w1 = u32CaptureB;

    if (capinput_pulse.w0 >= capinput_pulse.w1 || !pin_state) {
        capinput_pulse.w0 = 0;
        capinput_pulse.w1 = 0;
    }

    sigbuf_put(&capinput_pulse);
}

/**
 * @brief  Config CM_TMR6_2 PWM capture input
 * @param  [in] TMR6x               Timer6 unit
 *  @arg CM_TMR6_x
 * @retval None
 */
static void ConfigCapture(CM_TMR6_TypeDef *TMR6x)
{
    stc_timer6_init_t stcTmr6Init;
    stc_irq_signin_config_t stcIrqRegiConf;
    stc_tmr6_buf_config_t stcBufConfig;

    (void)TMR6_StructInit(&stcTmr6Init);
    (void)TMR6_BufFuncStructInit(&stcBufConfig);

    /* Timer6 general count function configuration */
    stcTmr6Init.sw_count.u32ClockDiv = TMR6_8_CLK_DIV;
    stcTmr6Init.u32PeriodValue = 0xFFFFu;
    (void)TMR6_Init(TMR6x, &stcTmr6Init);

    ssi.pulse_scale = get_tmr6_input_capture_time_scale(TMR6x);
    ssi.pulse_2ms = 2000 / ssi.pulse_scale;

    /* General compare buffer function configurate */
    stcBufConfig.u32BufNum = TMR6_BUF_SINGLE;
    stcBufConfig.u32BufTransCond = TMR6_BUF_TRANS_OVF;
    (void)TMR6_GeneralBufConfig(TMR6x, TMR6_CH_A, &stcBufConfig);
    (void)TMR6_GeneralBufConfig(TMR6x, TMR6_CH_B, &stcBufConfig);
    TMR6_GeneralBufCmd(TMR6x, TMR6_CH_A, ENABLE);
    TMR6_GeneralBufCmd(TMR6x, TMR6_CH_B, ENABLE);

    /* Capture input pin configuration */
    (void)TMR6_SetFilterClockDiv(TMR6x, TMR6_IO_PWMA, TMR6_FILTER_CLK_DIV16);
    TMR6_FilterCmd(TMR6x, TMR6_IO_PWMA, ENABLE);

    TMR6_SetFunc(TMR6x, TMR6_CH_A, TMR6_PIN_CAPT_INPUT);
    TMR6_SetFunc(TMR6x, TMR6_CH_B, TMR6_PIN_CAPT_INPUT);

    /* Hardware capture: Timer6 PWMA pin rising */
    TMR6_HWCaptureCondCmd(TMR6x, TMR6_CH_A, TMR6_CAPT_COND_PWMA_FAILLING, ENABLE);
    TMR6_HWCaptureCondCmd(TMR6x, TMR6_CH_B, TMR6_CAPT_COND_PWMA_RISING, ENABLE);

    /* HW Clear: Timer6 PWMA pin fall edge */
    TMR6_HWClearCondCmd(TMR6x, TMR6_CLR_COND_PWMA_RISING, ENABLE);
    TMR6_HWClearCmd(TMR6x, ENABLE);

    TMR6_HWStartCondCmd(TMR6x, TMR6_START_COND_PWMA_RISING, ENABLE);
    TMR6_HWStartCmd(TMR6x, ENABLE);

    /* Enable CM_TMR6_8 GCMAR interrupt */
    TMR6_IntCmd(TMR6x, TMR6_INT_MATCH_B, ENABLE);

    stcIrqRegiConf.enIRQn = EXAMPLE_CAPT_IRQN;
    stcIrqRegiConf.enIntSrc = EXAMPLE_CAPT_INT_SRC;
    stcIrqRegiConf.pfnCallback = &Tmr6_8_CapInputCallBack;
    (void)INTC_IrqSignIn(&stcIrqRegiConf);
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIO_04);
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}

// tick -> us
static float get_tmr6_input_capture_time_scale(CM_TMR6_TypeDef *TMR6x)
{
    uint32_t u32clkFreq;
    uint16_t u16Div;

    stc_clock_freq_t stcClockFreq;
    CLK_GetClockFreq(&stcClockFreq);

    u32clkFreq = stcClockFreq.u32Pclk0Freq;

    u16Div = READ_REG32(TMR6x->GCONR) & TMR6_GCONR_CKDIV;

    switch (u16Div)
    {
        case (TMR6_CLK_DIV2):
            u32clkFreq /= 2;
            break;
        case (TMR6_CLK_DIV4):
            u32clkFreq /= 4;
            break;
        case (TMR6_CLK_DIV8):
            u32clkFreq /= 8;
            break;
        case (TMR6_CLK_DIV16):
            u32clkFreq /= 16;
            break;
        case (TMR6_CLK_DIV32):
            u32clkFreq /= 32;
            break;
        case (TMR6_CLK_DIV64):
            u32clkFreq /= 64;
            break;
        case (TMR6_CLK_DIV128):
            u32clkFreq /= 128;
            break;
        case (TMR6_CLK_DIV256):
            u32clkFreq /= 256;
            break;
        case (TMR6_CLK_DIV512):
            u32clkFreq /= 512;
            break;
        case (TMR6_CLK_DIV1024):
            u32clkFreq /= 1024;
            break;
        case (TMR6_CLK_DIV1):
        default:
            break;
    }
    return 1e6f / u32clkFreq;
}

/*------------------------------------test------------------------------------*/
#endif

